Skip to content

Conversation

@Lancern
Copy link
Member

@Lancern Lancern commented Jul 30, 2025

This patch adds CIRGen support for cir.unreachable and cir.trap. It also adds missing LLVM lowering code for cir.unreachable.

@llvmbot llvmbot added clang Clang issues not falling into any other category ClangIR Anything related to the ClangIR project labels Jul 30, 2025
@llvmbot
Copy link
Member

llvmbot commented Jul 30, 2025

@llvm/pr-subscribers-clang

@llvm/pr-subscribers-clangir

Author: Sirui Mu (Lancern)

Changes

This patch adds CIRGen support for cir.unreachable and cir.trap. It also adds missing LLVM lowering code for cir.unreachable.


Full diff: https://github.com/llvm/llvm-project/pull/151363.diff

6 Files Affected:

  • (modified) clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp (+17)
  • (modified) clang/lib/CIR/CodeGen/CIRGenExpr.cpp (+5)
  • (modified) clang/lib/CIR/CodeGen/CIRGenFunction.h (+4)
  • (modified) clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp (+9-1)
  • (modified) clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h (+10)
  • (modified) clang/test/CIR/CodeGen/builtin_call.cpp (+24)
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
index 9049a016b2b9b..735b2b0a1627d 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
@@ -21,6 +21,7 @@
 #include "mlir/Support/LLVM.h"
 #include "clang/AST/Expr.h"
 #include "clang/AST/GlobalDecl.h"
+#include "clang/Basic/Builtins.h"
 #include "clang/CIR/MissingFeatures.h"
 #include "llvm/Support/ErrorHandling.h"
 
@@ -269,6 +270,22 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
   case Builtin::BI__builtin_rotateright32:
   case Builtin::BI__builtin_rotateright64:
     return emitRotate(e, /*isRotateLeft=*/false);
+
+  case Builtin::BI__builtin_trap: {
+    builder.create<cir::TrapOp>(loc);
+    // Note that cir.trap is a terminator so we need to start a new dummy block
+    // to preserve the builder's insertion point.
+    builder.createBlock(builder.getBlock()->getParent());
+    return RValue::get(nullptr);
+  }
+
+  case Builtin::BI__builtin_unreachable: {
+    emitUnreachable(e->getExprLoc());
+    // Note that cir.unreachable is a terminator so we need to start a new dummy
+    // block to preserve the builder's insertion point.
+    builder.createBlock(builder.getBlock()->getParent());
+    return RValue::get(nullptr);
+  }
   }
 
   // If this is an alias for a lib function (e.g. __builtin_sin), emit
diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
index c18498f80e99f..ae8da71219bf3 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
@@ -1780,6 +1780,11 @@ LValue CIRGenFunction::emitLoadOfReferenceLValue(Address refAddr,
                         pointeeBaseInfo);
 }
 
+void CIRGenFunction::emitUnreachable(clang::SourceLocation loc) {
+  assert(!cir::MissingFeatures::sanitizers());
+  builder.create<cir::UnreachableOp>(getLoc(loc));
+}
+
 mlir::Value CIRGenFunction::createDummyValue(mlir::Location loc,
                                              clang::QualType qt) {
   mlir::Type t = convertType(qt);
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index 603f75078c519..01049424f90cb 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -1201,6 +1201,10 @@ class CIRGenFunction : public CIRGenTypeCache {
 
   LValue emitUnaryOpLValue(const clang::UnaryOperator *e);
 
+  /// Emit a reached-unreachable diagnostic if \p loc is valid and runtime
+  /// checking is enabled. Otherwise, just emit an unreachable instruction.
+  void emitUnreachable(clang::SourceLocation loc);
+
   /// This method handles emission of any variable declaration
   /// inside a function, including static vars etc.
   void emitVarDecl(const clang::VarDecl &d);
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index 957a51ab334aa..ce245681e4443 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -2214,7 +2214,8 @@ void ConvertCIRToLLVMPass::runOnOperation() {
                CIRToLLVMVecShuffleDynamicOpLowering,
                CIRToLLVMVecShuffleOpLowering,
                CIRToLLVMVecSplatOpLowering,
-               CIRToLLVMVecTernaryOpLowering
+               CIRToLLVMVecTernaryOpLowering,
+               CIRToLLVMUnreachableOpLowering
       // clang-format on
       >(converter, patterns.getContext());
 
@@ -2270,6 +2271,13 @@ mlir::LogicalResult CIRToLLVMGetMemberOpLowering::matchAndRewrite(
   }
 }
 
+mlir::LogicalResult CIRToLLVMUnreachableOpLowering::matchAndRewrite(
+    cir::UnreachableOp op, OpAdaptor adaptor,
+    mlir::ConversionPatternRewriter &rewriter) const {
+  rewriter.replaceOpWithNewOp<mlir::LLVM::UnreachableOp>(op);
+  return mlir::success();
+}
+
 mlir::LogicalResult CIRToLLVMTrapOpLowering::matchAndRewrite(
     cir::TrapOp op, OpAdaptor adaptor,
     mlir::ConversionPatternRewriter &rewriter) const {
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
index f339d4310ae0c..c5106cb33f452 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
@@ -402,6 +402,16 @@ class CIRToLLVMGetMemberOpLowering
                   mlir::ConversionPatternRewriter &) const override;
 };
 
+class CIRToLLVMUnreachableOpLowering
+    : public mlir::OpConversionPattern<cir::UnreachableOp> {
+public:
+  using mlir::OpConversionPattern<cir::UnreachableOp>::OpConversionPattern;
+
+  mlir::LogicalResult
+  matchAndRewrite(cir::UnreachableOp op, OpAdaptor,
+                  mlir::ConversionPatternRewriter &) const override;
+};
+
 class CIRToLLVMTrapOpLowering : public mlir::OpConversionPattern<cir::TrapOp> {
 public:
   using mlir::OpConversionPattern<cir::TrapOp>::OpConversionPattern;
diff --git a/clang/test/CIR/CodeGen/builtin_call.cpp b/clang/test/CIR/CodeGen/builtin_call.cpp
index d9a70683a4dbc..8465ea83d7875 100644
--- a/clang/test/CIR/CodeGen/builtin_call.cpp
+++ b/clang/test/CIR/CodeGen/builtin_call.cpp
@@ -166,3 +166,27 @@ void expect_prob(int x, int y) {
 // LLVM-NEXT:    %[[Y_LONG:.+]] = sext i32 %[[Y]] to i64
 // LLVM-NEXT:    %{{.+}} = call i64 @llvm.expect.with.probability.i64(i64 %[[X_LONG]], i64 %[[Y_LONG]], double 2.500000e-01)
 // LLVM:       }
+
+void unreachable() {
+  __builtin_unreachable();
+}
+
+// CIR-LABEL: @_Z11unreachablev
+// CIR:         cir.unreachable
+// CIR:       }
+
+// LLVM-LABEL: @_Z11unreachablev
+// LLVM:         unreachable
+// LLVM:       }
+
+void trap() {
+  __builtin_trap();
+}
+
+// CIR-LABEL: @_Z4trapv
+// CIR:         cir.trap
+// CIR:       }
+
+// LLVM-LABEL: @_Z4trapv
+// LLVM:         call void @llvm.trap()
+// LLVM:       }

Comment on lines 276 to 278
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exercise this in tests, so that additional block is really required. Same for unreachable below.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also is this really needed? Classic codegen adds additional block only for unreachable.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also it is unclear to me why this one is inlined while unreachable uses emitUnreachable as in classic codegen. We should probably add emitTrap too.

Copy link
Member Author

@Lancern Lancern Jul 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also is this really needed? Classic codegen adds additional block only for unreachable.

In the classic CodeGen, calls to __builtin_unreachable gets lowered to the unreachable instruction, and calls to __builtin_trap gets lowered to an intrinsic call to @llvm.trap(). The former is a terminator instruction, which prohibits any further instructions from following it in the same block. The latter is not a terminator and it could be followed by more instructions in the same block (these instructions would be DCE-ed though). So in the classic CodeGen we don't have to start a new block after calling __builtin_trap.

On the contrary, in CIR world, both of cir.unreachable and cir.trap are terminators. Thus we have to start a new block after them to hold those instructions that follow them.

Also it is unclear to me why this one is inlined while unreachable uses emitUnreachable as in classic codegen. We should probably add emitTrap too.

emitUnreachable is there because we have a sanitizer for unreachable code. We don't have sanitizers for traps, so we just inlined the creation of cir.trap.

Maybe we could put builder.createBlock in emitUnreachable and emitTrap though.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we could put builder.createBlock in emitUnreachable and emitTrap though.

I updated code like this.

@Lancern Lancern force-pushed the cir/builtin-traps branch from 22fd432 to 8f1deb8 Compare July 31, 2025 16:19
Copy link
Contributor

@andykaylor andykaylor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm, with a couple of minor comments

@Lancern Lancern force-pushed the cir/builtin-traps branch from 8f1deb8 to 6e74f19 Compare August 2, 2025 08:57
@Lancern Lancern force-pushed the cir/builtin-traps branch from 6e74f19 to 8f9ec79 Compare August 2, 2025 09:06
Copy link
Contributor

@xlauko xlauko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@Lancern Lancern merged commit 13600c7 into llvm:main Aug 5, 2025
9 checks passed
@Lancern Lancern deleted the cir/builtin-traps branch August 5, 2025 12:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

clang Clang issues not falling into any other category ClangIR Anything related to the ClangIR project

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants